IndexedElementRecordMapping.java
package org.codefilarete.stalactite.engine.configurer.elementcollection;
import java.util.HashMap;
import java.util.Map;
import org.codefilarete.reflection.ReadWriteAccessPoint;
import org.codefilarete.stalactite.mapping.ComposedIdMapping;
import org.codefilarete.stalactite.mapping.DefaultEntityMapping;
import org.codefilarete.stalactite.mapping.EmbeddedClassMapping;
import org.codefilarete.stalactite.mapping.IdAccessor;
import org.codefilarete.stalactite.mapping.IdMapping;
import org.codefilarete.stalactite.mapping.id.assembly.ComposedIdentifierAssembler;
import org.codefilarete.stalactite.mapping.id.assembly.IdentifierAssembler;
import org.codefilarete.stalactite.mapping.id.manager.AlreadyAssignedIdentifierManager;
import org.codefilarete.stalactite.query.api.Selectable;
import org.codefilarete.stalactite.sql.ddl.structure.Column;
import org.codefilarete.stalactite.sql.ddl.structure.Table;
import org.codefilarete.stalactite.sql.result.ColumnedRow;
import org.codefilarete.tool.collection.Maps;
/**
* Class mapping dedicated to {@link IndexedElementRecord}. Very close to {@link ElementRecordMapping}
* in its principle.
*/
public class IndexedElementRecordMapping<C, I, T extends Table<T>, ER extends IndexedElementRecord<C, I>> extends DefaultEntityMapping<ER, ER, T> {
public <LEFTTABLE extends Table<LEFTTABLE>> IndexedElementRecordMapping(T targetTable,
Column<T, C> elementColumn,
Column<T, Integer> indexColumn,
IdentifierAssembler<I, LEFTTABLE> sourceIdentifierAssembler,
Map<Column<LEFTTABLE, ?>, Column<T, ?>> foreignKeyColumnMapping) {
super((Class) IndexedElementRecord.class,
targetTable,
(Map) Maps.forHashMap(ReadWriteAccessPoint.class, Column.class)
.add(ElementRecord.ELEMENT_ACCESSOR, elementColumn)
.add(IndexedElementRecord.INDEX_ACCESSOR, indexColumn),
new IndexedElementRecordIdMapping<>(targetTable, elementColumn, indexColumn, sourceIdentifierAssembler, foreignKeyColumnMapping));
}
public <LEFTTABLE extends Table<LEFTTABLE>> IndexedElementRecordMapping(T targetTable,
EmbeddedClassMapping<ER, T> embeddableMapping,
Column<T, Integer> indexColumn,
IdentifierAssembler<I, LEFTTABLE> sourceIdentifierAssembler,
Map<Column<LEFTTABLE, ?>, Column<T, ?>> foreignKeyColumnMapping) {
super((Class) IndexedElementRecord.class,
targetTable,
(Map) Maps.putAll(Maps.forHashMap(ReadWriteAccessPoint.class, Column.class)
.add(IndexedElementRecord.INDEX_ACCESSOR, indexColumn), embeddableMapping.getPropertyToColumn()),
new IndexedElementRecordIdMapping<>(targetTable, embeddableMapping, indexColumn, sourceIdentifierAssembler, foreignKeyColumnMapping));
}
/**
* {@link IdMapping} for {@link IndexedElementRecord} : a composed id made of :
* - {@link IndexedElementRecord#getId()}
* - {@link IndexedElementRecord#getIndex()}
* - {@link IndexedElementRecord#getElement()}
*/
public static class IndexedElementRecordIdMapping<C, I, T extends Table<T>, ER extends IndexedElementRecord<C, I>> extends ComposedIdMapping<ER, ER> {
public <LEFTTABLE extends Table<LEFTTABLE>> IndexedElementRecordIdMapping(
T targetTable,
Column<T, C> elementColumn,
Column<T, Integer> indexColumn,
IdentifierAssembler<I, LEFTTABLE> sourceIdentifierAssembler,
Map<Column<LEFTTABLE, ?>, Column<T, ?>> foreignKeyColumnMapping) {
super(new IndexedElementRecordIdAccessor<>(),
new AlreadyAssignedIdentifierManager<>((Class<ER>) (Class) IndexedElementRecord.class,
IndexedElementRecord::markAsPersisted,
IndexedElementRecord::isPersisted),
new DefaultIndexedElementRecordIdentifierAssembler<>(targetTable, elementColumn, indexColumn, sourceIdentifierAssembler, foreignKeyColumnMapping));
}
public <LEFTTABLE extends Table<LEFTTABLE>> IndexedElementRecordIdMapping(
T targetTable,
EmbeddedClassMapping<ER, T> elementMapping,
Column<T, Integer> indexColumn,
IdentifierAssembler<I, LEFTTABLE> sourceIdentifierAssembler,
Map<Column<LEFTTABLE, ?>, Column<T, ?>> foreignKeyColumnMapping) {
super(new IndexedElementRecordIdAccessor<>(),
new AlreadyAssignedIdentifierManager<>((Class<ER>) (Class) IndexedElementRecord.class,
IndexedElementRecord::markAsPersisted,
IndexedElementRecord::isPersisted),
new IndexedElementRecordIdentifierAssembler<>(targetTable, elementMapping, indexColumn, sourceIdentifierAssembler, foreignKeyColumnMapping));
}
/**
* Override because {@link ComposedIdMapping} is based on null identifier to determine newness, which is always false for {@link IndexedElementRecord}
* because they always have one. We delegate its computation to the entity.
*
* @param entity any non-null entity
* @return true or false based on {@link IndexedElementRecord#isNew()}
*/
@Override
public boolean isNew(ER entity) {
return entity.isNew();
}
private static class IndexedElementRecordIdAccessor<C, I, ER extends IndexedElementRecord<C, I>> implements IdAccessor<ER, ER> {
@Override
public ER getId(ER associationRecord) {
return associationRecord;
}
@Override
public void setId(ER associationRecord, ER identifier) {
associationRecord.setId(identifier.getId());
associationRecord.setIndex(identifier.getIndex());
associationRecord.setElement(identifier.getElement());
}
}
/**
* Identifier assembler when {@link IndexedElementRecord} is persisted according to a default behavior :
* - identifier is saved in table primary key
* - element value is saved in elementColumn
*
* @param <TRGT> embedded bean type
* @param <SRCID> source identifier type
*/
public static class DefaultIndexedElementRecordIdentifierAssembler<TRGT, SRCID, T extends Table<T>, ER extends IndexedElementRecord<TRGT, SRCID>> extends ComposedIdentifierAssembler<ER, T> {
private final Column<T, TRGT> elementColumn;
private final Column<T, Integer> indexColumn;
private final IdentifierAssembler<SRCID, ?> sourceIdentifierAssembler;
private final Map<Column<?, ?>, Column<T, ?>> foreignKeyColumnMapping;
private <LEFTTABLE extends Table<LEFTTABLE>> DefaultIndexedElementRecordIdentifierAssembler(T targetTable,
Column<T, TRGT> elementColumn,
Column<T, Integer> indexColumn,
IdentifierAssembler<SRCID, LEFTTABLE> sourceIdentifierAssembler,
Map<Column<LEFTTABLE, ?>, Column<T, ?>> foreignKeyColumnMapping) {
super(targetTable);
this.elementColumn = elementColumn;
this.indexColumn = indexColumn;
this.sourceIdentifierAssembler = sourceIdentifierAssembler;
this.foreignKeyColumnMapping = (Map) foreignKeyColumnMapping;
}
public Column<T, TRGT> getElementColumn() {
return elementColumn;
}
public IdentifierAssembler<SRCID, ?> getSourceIdentifierAssembler() {
return sourceIdentifierAssembler;
}
@Override
public ER assemble(ColumnedRow columnValueProvider) {
SRCID leftValue = sourceIdentifierAssembler.assemble(new ColumnedRow() {
@Override
public <E> E get(Selectable<E> sourceColumn) {
Column<T, E> targetColumn = (Column<T, E>) foreignKeyColumnMapping.get(sourceColumn);
return columnValueProvider.get(targetColumn);
}
});
TRGT rightValue = columnValueProvider.get(elementColumn);
// we should not return an id if any (both expected in fact) value is null
if (leftValue == null || rightValue == null) {
return null;
} else {
return (ER) new IndexedElementRecord<>(leftValue, rightValue, columnValueProvider.get(indexColumn));
}
}
@Override
public Map<Column<T, ?>, Object> getColumnValues(ER id) {
Map<Column<?, ?>, Object> sourceColumnValues = (Map) sourceIdentifierAssembler.getColumnValues(id.getId());
Map<Column<T, ?>, Object> idColumnValues = Maps.innerJoin(foreignKeyColumnMapping, sourceColumnValues);
Map<Column<T, ?>, Object> result = new HashMap<>();
result.put(indexColumn, id.getIndex());
result.putAll(idColumnValues);
return result;
}
}
/**
* Identifier assembler for cases where user gave a configuration to persist embedded beans (default way is not used)
*
* @param <TRGT> embedded bean type
* @param <SRCID> source identifier type
*/
public static class IndexedElementRecordIdentifierAssembler<TRGT, SRCID, T extends Table<T>, ER extends IndexedElementRecord<TRGT, SRCID>> extends ComposedIdentifierAssembler<ER, T> {
private final EmbeddedClassMapping<ER, T> elementMapping;
private final IdentifierAssembler<SRCID, ?> sourceIdentifierAssembler;
private final Map<Column<?, ?>, Column<T, ?>> foreignKeyColumnMapping;
private Column<T, ?> indexColumn;
private <LEFTTABLE extends Table<LEFTTABLE>> IndexedElementRecordIdentifierAssembler(
T targetTable,
EmbeddedClassMapping<ER, T> elementMapping,
Column<T, ?> indexColumn,
IdentifierAssembler<SRCID, LEFTTABLE> sourceIdentifierAssembler,
Map<Column<LEFTTABLE, ?>, Column<T, ?>> foreignKeyColumnMapping) {
super(targetTable);
this.elementMapping = elementMapping;
this.sourceIdentifierAssembler = sourceIdentifierAssembler;
this.foreignKeyColumnMapping = (Map) foreignKeyColumnMapping;
this.indexColumn = indexColumn;
}
public EmbeddedClassMapping<ER, T> getElementMapping() {
return elementMapping;
}
@Override
public ER assemble(ColumnedRow row) {
return elementMapping.transform(row);
}
@Override
public Map<Column<T, ?>, Object> getColumnValues(ER id) {
Map<Column<?, ?>, Object> sourceColumnValues = (Map) sourceIdentifierAssembler.getColumnValues(id.getId());
Map<Column<T, ?>, Object> idColumnValues = Maps.innerJoin(foreignKeyColumnMapping, sourceColumnValues);
Map<Column<T, ?>, Object> result = new HashMap<>();
result.putAll(idColumnValues);
result.put(indexColumn, id.getIndex());
return result;
}
}
}
}